/**

This file is part of MaCI/GIMnet.

MaCI/GIMnet is free software: you can redistribute it and/or modify it 
under the terms of the GNU Lesser General Public License as published 
by the Free Software Foundation, either version 3 of the License, or 
(at your option) any later version.

MaCI/GIMnet is distributed in the hope that it will be useful, but WITHOUT 
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 
FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public 
License for more details.

You should have received a copy of the GNU Lesser General Public 
License along with GIMnet. (See COPYING.LESSER) If not, see 
<http://www.gnu.org/licenses/>.

**/
/**
 *
 * $Id: AudioData.cpp,v 1.1 2009-10-16 13:24:11 morsko Exp $
 *
 * \file
 * \brief Implementation for Audio interface data encoder/decoder.
 * \author Matthieu Myrsky <matthieu.myrsky@tkk.fi>
 * \NOTE: Not ready yet!
 */

#include "AudioData.hpp"
#include "AudioContainer.hpp"
#include "binbag.h"
#include "owndebug.h"


//*****************************************************************************
using namespace MaCI::Audio;
//*****************************************************************************
//*****************************************************************************
//***************************************************************************** 

CAudioData::CAudioData(void)
  : iValid(false),
    iTimestampPtr(NULL),
    iCommandPtr(NULL),
    iAudioDataPtr(NULL),
    iAudioInfo(NULL)
{
}   
//*****************************************************************************

CAudioData::~CAudioData(void)
{


}
//*****************************************************************************     

    
CAudioData::CAudioData(const CAudioData &aAudioData)
  :iValid(false),
  iTimestampPtr(NULL),
  iCommandPtr(NULL),
  iAudioDataPtr(NULL),
  iAudioInfo(NULL)
{
  *this = aAudioData;
}
//*****************************************************************************
      

CAudioData &CAudioData::operator=(const CAudioData &aAudioData)
{
  // Create the internal binbag from source data.
  CreateInternalBinBag(aAudioData.iBinBagContainer);
  
  // Do the decode on the buffer; it sets pointers and
  if (DecodeFrom(NULL) == false) {
    dPrint(4,"WARNING: Failed to Decode AudioData even though assignment operator used?");
  }

  return *this; 
}
//*****************************************************************************


bool CAudioData::DecodeFrom(gim::binbag::CBinBag *aBinBag)
{
  bool result = true;
  const gim::binbag::TBinBlob *b = NULL;
  int previous_type = -1;
  
  // Store pointer
  if (aBinBag != NULL)  
    iBinBagContainer = aBinBag;

  // Clear all pointers before starting to Decode.
  SetReadOnlyContainer(false);
  iValid =false;
  iTimestampPtr = NULL;
  iCommandPtr = NULL;
  iAudioDataPtr = NULL;
  iAudioInfo = NULL;
  SetLastElementType(KTypeUnknown);
  
  // Parse
  if (iBinBagContainer && iBinBagContainer->Rewind()) {
    while( (b = iBinBagContainer->GetNextBinary()) && result ) {
      switch(b->type) {
      case KTypeTimestamp:
	if (iTimestampPtr == NULL) {
	  iTimestampPtr = reinterpret_cast<const Common::TTimestamp *>(b->data);

	} else {
          dPrint(4,"Timestamp element already received. Decoding failed.");
          result = false;
        
        }
	break;
	
      case KTypeCommand:
	if (iCommandPtr == NULL) {
	  iCommandPtr = reinterpret_cast<const TCommand *>(b->data);

	} else {
          dPrint(4,"Command element already received. Decoding failed.");
          result = false;
        
        }
	break;
        
      case KTypeAudioInfo:
	if (iAudioInfo == NULL) {
	  iAudioInfo = reinterpret_cast<const TAudioInfo *>(b->data);
	} else {
          dPrint(4,"Audio element already received. Decoding failed.");
          result = false;
        
        }
	break;
        
      case KTypeAudioData:
        if (iAudioDataPtr == NULL) {
          iAudioDataPtr = b;
        } else {
          dPrint(4,"Audio element already received. Decoding failed.");
          result = false;
          
        }
        break;

      
        
      default:
	// Unknown Type in BinBag. Do nothing. (Ignore for compability)
        dPrint(4,"Unknown type '%u' in container. Ignoring.", b->type);
	break;
      }
      
      // Store previous type
      previous_type = b->type;
    }
  } else result = false;
  
  // Store the result
  SetReadOnlyContainer( (iValid = result) );
  
  return result;
}
//*****************************************************************************
void CAudioData::Reset()
{
  CMaCIData::Reset();
  iValid =false;
  iTimestampPtr = NULL;
  iCommandPtr = NULL;
  iAudioDataPtr = NULL;
  iAudioInfo = NULL;
}
//*****************************************************************************
void CAudioData::Print(const int level) const
{
  dPrint(1,"not implemented..");
}
//*****************************************************************************
     
bool CAudioData::SetTimestamp(const Common::TTimestamp &aStamp)
{
  bool result = false;
  if (!IsReadOnlyContainer() && iTimestampPtr == NULL) {

    ADDELEMENT(Common::TTimestamp, KTypeTimestamp, iTimestampPtr, aStamp);
    result = true;
  }
  return result;
}
//*****************************************************************************

    
bool CAudioData::SetCommand(const TCommand &aCmd)
{
  bool result = false;
  if (!IsReadOnlyContainer() && iCommandPtr == NULL) {

    ADDELEMENT(TCommand, KTypeCommand, iCommandPtr, aCmd);
    result = true;
  }
  return result;
}
//*****************************************************************************

bool CAudioData::SetAudioInfo(const TAudioInfo &aInfo)
{
  bool result = false;
  if (!IsReadOnlyContainer() && iAudioInfo == NULL) {

    ADDELEMENT(TAudioInfo, KTypeAudioInfo, iAudioInfo, aInfo);
    result = true;
  }
  return result;
}
//*****************************************************************************

bool CAudioData::AddAudio(CAudioContainer &aAudioData)
{
  bool result = false;
  if (!IsReadOnlyContainer() &&
      iAudioDataPtr == NULL){
    // Now, add the real image data.
    unsigned char *dataptr;
    unsigned int datasize = 0; 
              
    aAudioData.GetAudioData(&dataptr,&datasize);

    iBinBagContainer->AddBinary(KTypeAudioData,
                                (const char *)dataptr,
                                datasize);

    SetLastElementType(KTypeAudioData);
    result = true;
  }

  return result;



}

//*****************************************************************************      
    
const MaCI::Common::TTimestamp *CAudioData::GetTimestamp(void) const
{
  return iTimestampPtr;
}
//*****************************************************************************

bool CAudioData::GetAudio(char **aAudioData,
                          unsigned int *aSize) const
{
  bool result = false;
  if (iValid && IsReadOnlyContainer() && iAudioDataPtr) {
    *aAudioData = (char *)iAudioDataPtr->data;
    *aSize = iAudioDataPtr->size;

    result = true;
  }
  return result;

}
//*****************************************************************************

const TCommand *CAudioData::GetCommand(void) const
{
  return iCommandPtr;
}
//*****************************************************************************

const TAudioInfo *CAudioData::GetAudioInfo(void) const
{ 
  return iAudioInfo;
}
//*****************************************************************************
//*****************************************************************************
//*****************************************************************************
//*****************************************************************************
//*****************************************************************************
//*****************************************************************************
//*****************************************************************************
//*****************************************************************************
#ifdef COMPILE_AUDIODATA_STANDALONE_MAIN

// Assert sequences verify that the container is working correctly
// (== The data read matches the data wrote in)
#include <assert.h>
#include "AudioContainer.hpp"
#include "../../drivers/Audio/ALSA/ALSA.hpp"

int main(void)
{
  using namespace MaCI::Audio;
  bool r;
  gim::binbag::CBinBag bb;
  unsigned int frames =32;
  unsigned int sampleRate = 44100;
  debugInit();
  debugSetGlobalDebugLvl(1);


  CAlsa alsa;
  alsa.SetParameters(sampleRate,
                     (unsigned int)2,
                     EAudioFormatPCM16Bit,
                     EDataAccessRWInterleaved,
                     EDirectionCapture,
                     frames);

  //Open alsa driver
  if(alsa.Open()){
    dPrint(1,"Succesfully opened Alsa Driver.");
  }else{
    dPrint(1,"Failed to open Alsa driver.");
  }

  unsigned int numberOfFrames = 0;
  alsa.GetPeriodSize(numberOfFrames);
  dPrint(1,"numberOfFrames %d",numberOfFrames);
  unsigned int size = numberOfFrames * 4*100; /* 2 bytes/sample, 2 channels */
  char *buffer = (char *) malloc(size);
  dPrint(1,"recording...");
  alsa.GetAudio(buffer, numberOfFrames*100);
  dPrint(1,"recording ended");
  
  dPrint(1,"adding to container");
  dPrint(1,"Size %u",size);
  CAudioContainer container;
  container.SetAudioData((unsigned char *)buffer,
                         size,
                         TAudioInfo(KAudioDataRaw,
                                    sampleRate,
                                    2,
                                    numberOfFrames),
                         KAudioDataRaw,
                         KAudioDataRaw);
  
  CAudioData A;
  CAudioData B;

  // Start test.
  printf("Carry out Encoding/Decoding test.\n");
  
  printf("Encoding...\n");
  // Do Encoding 
  r = A.EncodeTo(&bb);
  assert(r == true);

  printf("Encode: Timestamp (1, 2)\n");
  r = A.SetTimestamp(MaCI::Common::TTimestamp(1, 2));
  assert(r == true);
  printf("OK.\n");
  
  printf("Encode: SetCommand ( KCommandNewAudio(%d) )\n", KCommandNewAudio);
  r = A.SetCommand(TCommand(KCommandNewAudio));
  assert(r == true);
  printf("OK.\n");
  
  printf("Encode:AddAudio\n");
  r = A.AddAudio(container);
  assert(r == true);
  printf("OK.\n");

  printf("\nDecoding...\n");
  // Do Decoding
  CAudioContainer container2;
  B.DecodeFrom(&bb);
  printf("Decode: Timestamp = (%d, %d)\n", 
         B.GetTimestamp()->timestamp_s, 
         B.GetTimestamp()->timestamp_us);
  assert(B.GetTimestamp()->timestamp_s == 1);
  assert(B.GetTimestamp()->timestamp_us == 2);

  printf("Decode: GetCommand() = (%d)\n", 
         B.GetCommand()->cmd);
  assert(B.GetCommand()->cmd == KCommandNewAudio);




  char *buffer2;
  unsigned int *size2;
  printf("Decode: GetAudio() \n");
  r = B.GetAudio(&buffer2,size2);  
  dPrint(1,"sizee %u", sizeof(buffer2));
  printf("Verifying data contents...");
  dPrint(1,"memcmp %d",memcmp((const char *)buffer, (const char *)buffer2, size));
  //assert(memcmp((const char *)buffer, (const char *)buffer2, size) == 0);
  printf("OK.\n");

  CAlsa alsa2;
  alsa2.SetParameters(sampleRate,
                     2,
                     EAudioFormatPCM16Bit,
                     EDataAccessRWInterleaved,
                     EDirectionPlayback,
                     frames);

  //Open alsa driver
  if(alsa2.Open()){
    dPrint(1,"Succesfully opened Alsa 2Driver.");
  }else{
    dPrint(1,"Failed to open Alsa2 driver.");
  }
  dPrint(1,"Playing...");
  alsa2.PlayAudio((char *)buffer2, numberOfFrames*100);
  dPrint(1,"Playing ended...");


  printf("\n\nTest completed OK.\n");
      
}
#endif
